home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir24 / jnos110g.zip / TCPOUT.C < prev    next >
C/C++ Source or Header  |  1994-04-17  |  8KB  |  221 lines

  1. /* TCP output segment processing
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. #include "global.h"
  5. #include "timer.h"
  6. #include "mbuf.h"
  7. #include "netuser.h"
  8. #include "internet.h"
  9. #include "iface.h"
  10. #include "tcp.h"
  11. #include "ip.h"
  12.   
  13. long Tcp_maxwait;
  14.   
  15. /* Send a segment on the specified connection. One gets sent only
  16.  * if there is data to be sent or if "force" is non zero
  17.  */
  18. void
  19. tcp_output(tcb)
  20. struct tcb *tcb;
  21. {
  22.     struct pseudo_header ph;/* Pseudo-header for checksum calcs */
  23.     struct mbuf *hbp,*dbp;  /* Header and data buffer pointers */
  24.     int16 hsize;        /* Size of header */
  25.     struct tcp seg;     /* Local working copy of header */
  26.     int16 ssize;        /* Size of current segment being sent,
  27.                  * including SYN and FIN flags */
  28.     int16 dsize;        /* Size of segment less SYN and FIN */
  29.     int16 usable;       /* Usable window */
  30.     int16 sent;     /* Sequence count (incl SYN/FIN) already
  31.                  * in the pipe but not yet acked */
  32.     int32 rto;      /* Retransmit timeout setting */
  33.   
  34.     if(tcb == NULLTCB)
  35.         return;
  36.   
  37.     switch(tcb->state){
  38.         case TCP_LISTEN:
  39.         case TCP_CLOSED:
  40.             return; /* Don't send anything */
  41.     }
  42.     for(;;){
  43.         /* Compute data already in flight */
  44.         sent = tcb->snd.ptr - tcb->snd.una;
  45.   
  46.         /* If transmitter has been idle for more than a RTT,
  47.          * take the congestion window back down to one packet.
  48.          */
  49.         if(!run_timer(&tcb->timer)
  50.             && (msclock() - tcb->lastactive) > tcb->srtt)
  51.             tcb->cwind = tcb->mss;
  52.   
  53.         /* Compute usable send window as minimum of offered
  54.          * and congestion windows, minus data already in flight.
  55.          * Be careful that the window hasn't shrunk --
  56.          * these are unsigned vars.
  57.          */
  58.         usable = min(tcb->snd.wnd,tcb->cwind);
  59.         if(usable > sent)
  60.             usable -= sent; /* Most common case */
  61.         else if(usable == 0 && sent == 0)
  62.             usable = 1; /* Closed window probe */
  63.         else
  64.             usable = 0; /* Window closed or shrunken */
  65.   
  66.         /* Compute size of segment we *could* send. This is the
  67.          * smallest of the usable window, the mss, or the amount
  68.          * we have on hand. (I don't like optimistic windows)
  69.          */
  70.         ssize = min(tcb->sndcnt - sent,usable);
  71.         ssize = min(ssize,tcb->mss);
  72.   
  73.         /* Now we decide if we actually want to send it.
  74.          * Apply John Nagle's "single outstanding segment" rule.
  75.          * If data is already in the pipeline, don't send
  76.          * more unless it is MSS-sized or the very last packet.
  77.          */
  78.         if(sent != 0 && ssize < tcb->mss
  79.         && !(tcb->state == TCP_FINWAIT1 && ssize == tcb->sndcnt-sent)){
  80.             ssize = 0;
  81.         }
  82.         /* Unless the tcp syndata option is on, inhibit data until
  83.          * our SYN has been acked. This ought to be OK, but some
  84.          * old TCPs have problems with data piggybacked on SYNs.
  85.          */
  86.         if(!tcb->flags.synack && !tcb->parms->syndata){
  87.             if(tcb->snd.ptr == tcb->iss)
  88.                 ssize = min(1,ssize);   /* Send only SYN */
  89.             else
  90.                 ssize = 0;  /* Don't send anything */
  91.         }
  92.         if(ssize == 0 && !tcb->flags.force)
  93.             break;      /* No need to send anything */
  94.   
  95.         tcb->flags.force = 0;   /* Only one forced segment! */
  96.   
  97.         seg.source = tcb->conn.local.port;
  98.         seg.dest = tcb->conn.remote.port;
  99.   
  100.         /* Set the flags according to the state we're in. It is
  101.          * assumed that if this segment is associated with a state
  102.          * transition, then the state change will already have been
  103.          * made. This allows this routine to be called from a
  104.          * retransmission timeout with force=1.
  105.          */
  106.         seg.flags.urg = 0; /* Not used in this implementation */
  107.         seg.flags.rst = 0;
  108.         seg.flags.ack = 1; /* Every state except TCP_SYN_SENT */
  109.         seg.flags.syn = 0; /* syn/fin/psh set later if needed */
  110.         seg.flags.fin = 0;
  111.         seg.flags.psh = 0;
  112.         seg.flags.congest = tcb->flags.congest;
  113.   
  114.         hsize = TCPLEN; /* Except when SYN being sent */
  115.         seg.mss = 0;
  116.         seg.optlen = 0;
  117.   
  118.         if(tcb->state == TCP_SYN_SENT)
  119.             seg.flags.ack = 0; /* Haven't seen anything yet */
  120.   
  121.         dsize = ssize;
  122.         if(!tcb->flags.synack && tcb->snd.ptr == tcb->iss){
  123.             /* Send SYN */
  124.             seg.flags.syn = 1;
  125.             dsize--;    /* SYN isn't really in snd queue */
  126.             /* Also send MSS */
  127.             seg.mss = tcb->parms->mss;
  128.             seg.optlen = 0;
  129.             hsize = TCPLEN + MSS_LENGTH;
  130.         }
  131.         seg.seq = tcb->snd.ptr;
  132.         seg.ack = tcb->rcv.nxt;
  133.         seg.wnd = tcb->rcv.wnd;
  134.         seg.up = 0;
  135.   
  136.         /* Now try to extract some data from the send queue. Since
  137.          * SYN and FIN occupy sequence space and are reflected in
  138.          * sndcnt but don't actually sit in the send queue, dup_p
  139.          * will return one less than dsize if a FIN needs to be sent.
  140.          */
  141.         if(dsize != 0){
  142.             int16 offset;
  143.   
  144.             /* SYN doesn't actually take up space on the sndq,
  145.              * so take it out of the sent count
  146.              */
  147.             offset = sent;
  148.             if(!tcb->flags.synack && sent != 0)
  149.                 offset--;
  150.   
  151.             if(dup_p(&dbp,tcb->sndq,offset,dsize) != dsize){
  152.                 /* We ran past the end of the send queue;
  153.                  * send a FIN
  154.                  */
  155.                 seg.flags.fin = 1;
  156.                 dsize--;
  157.             }
  158.         } else {
  159.             dbp = NULLBUF;
  160.         }
  161.         /* If the entire send queue will now be in the pipe, set the
  162.          * push flag
  163.          */
  164.         if(dsize != 0 && sent + ssize == tcb->sndcnt)
  165.             seg.flags.psh = 1;
  166.   
  167.         /* If this transmission includes previously transmitted data,
  168.          * snd.nxt will already be past snd.ptr. In this case,
  169.          * compute the amount of retransmitted data and keep score
  170.          */
  171.         if(tcb->snd.ptr < tcb->snd.nxt)
  172.             tcb->resent += min(tcb->snd.nxt - tcb->snd.ptr,ssize);
  173.   
  174.         tcb->snd.ptr += ssize;
  175.         /* If this is the first transmission of a range of sequence
  176.          * numbers, record it so we'll accept acknowledgments
  177.          * for it later
  178.          */
  179.         if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
  180.             tcb->snd.nxt = tcb->snd.ptr;
  181.   
  182.         /* Fill in fields of pseudo IP header */
  183.         ph.source = tcb->conn.local.address;
  184.         ph.dest = tcb->conn.remote.address;
  185.         ph.protocol = TCP_PTCL;
  186.         ph.length = hsize + dsize;
  187.   
  188.         /* Generate TCP header, compute checksum, and link in data */
  189.         if((hbp = htontcp(&seg,dbp,&ph)) == NULLBUF){
  190.             free_p(dbp);
  191.             return;
  192.         }
  193.         /* If we're sending some data or flags, start retransmission
  194.          * and round trip timers if they aren't already running.
  195.          */
  196.         if(ssize != 0){
  197.             /* Set round trip timer. */
  198.             rto = backoff(tcb) * (4 * tcb->mdev + tcb->srtt);
  199.             if(tcb->parms->maxwait && rto > tcb->parms->maxwait)
  200.                 rto = tcb->parms->maxwait;
  201.             set_timer(&tcb->timer,max(MIN_RTO,rto));
  202.             if(!run_timer(&tcb->timer))
  203.                 start_timer(&tcb->timer);
  204.   
  205.             /* If round trip timer isn't running, start it */
  206.             if(!tcb->flags.rtt_run){
  207.                 tcb->flags.rtt_run = 1;
  208.                 tcb->rtt_time = msclock();
  209.                 tcb->rttseq = tcb->snd.ptr;
  210.             }
  211.         }
  212.         if(tcb->flags.retran)
  213.             tcpRetransSegs++;
  214.         else
  215.             tcpOutSegs++;
  216.   
  217.         ip_send(tcb->conn.local.address,tcb->conn.remote.address,
  218.         TCP_PTCL,tcb->tos,0,hbp,ph.length,0,0);
  219.     }
  220. }
  221.